//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_DATETIME_CLOCKS_H_
#define _BUOLA_DATETIME_CLOCKS_H_

#include <buola/buola.h>
#include <buola/datetime/chrono.h>
#include <ctime>

namespace buola {

///\addtogroup datetime
///@{

#if BUOLA_PLATFORM_LINUX

////////////////////////////////////////////////////////////////////////////

/// monotonic clock

/// This class can be used to get the current time with respect to some
/// system-defined monotonically increasing clock.
///
/// While this clock cannot be used to get the current calendar time, it
/// is faster and more precise than calendar clock in some systems, and it
/// cannot be set, so the time is guaranteed to be monotonically increasing.
///
////////////////////////////////////////////////////////////////////////////

class CMonotonicClock
{
public:
    typedef chrono::nanoseconds duration;
    typedef duration::rep rep;
    typedef duration::period period;
    typedef chrono::time_point<CMonotonicClock> time_point;
    static const bool is_steady=true;

    ///returns the current time
    static time_point now()
    {
        struct timespec lTS;

        clock_gettime(CLOCK_MONOTONIC,&lTS);

        return time_point(chrono::nanoseconds(1000000000L*(rep)lTS.tv_sec+(rep)lTS.tv_nsec));
    }
};

#else //BUOLA_PLATFORM_LINUX

///monotonic clock
typedef chrono::steady_clock CMonotonicClock;

#endif //BUOLA_PLATFORM_LINUX

///represents a point in time in monotonic time
typedef CMonotonicClock::time_point CMonotonicTime;

///calendar clock
typedef chrono::system_clock CCalendarClock;

///represents a point in time in calendar (system) time
typedef CCalendarClock::time_point CCalendarTime;

///overload of sleep that takes a std::chrono duration as an argument
inline void sleep(const chrono::nanoseconds &pDuration)
{
    struct timespec lSpec;
    lSpec.tv_sec=pDuration.count()/1000000000L;
    lSpec.tv_nsec=pDuration.count()%1000000000L;
    while(nanosleep(&lSpec,&lSpec)!=0) {}
}

namespace detail {

    struct CTimeDurationReturn
    {
        CTimeDurationReturn(double pTime)
            :   mTime(pTime)
        {}

        template<typename tRep,typename tPeriod>
        operator chrono::duration<tRep,tPeriod>()
        {
            return chrono::duration<tRep,tPeriod>((tRep)(mTime*(double)tPeriod::den/(double)tPeriod::num));
        }

    private:
        double mTime;
    };

/* namespace detail */ }

///returns a time duration from a floating-point number of seconds
inline detail::CTimeDurationReturn time_duration(double pSec)
{
    return detail::CTimeDurationReturn(pSec);
}

///returns a floating-point number of seconds from a time duration
template<typename tRep,typename tPeriod>
inline double to_double_seconds(const chrono::duration<tRep,tPeriod> &pDuration)
{
    return (double)pDuration.count()*(double)tPeriod::num/(double)tPeriod::den;
}

///@}

/*namespace buola*/ }

#endif
